﻿using Mirror.Examples.Benchmark;
using System;
using System.Collections;
using UnityEngine;


public class Swing : MonoBehaviour
{
    PlayerMovement playerMovement;
    Rigidbody rb;
    Vector2 inputDirection;
    bool swingRequest;
    bool jumpRequest;
    public LineRenderer lr;
    public Transform gunTip, cam, orientation;
    public LayerMask whatIsGarppleable;
    [Header("Swinging")]
    public float maxSwingDistance = 50;
    public Vector3 swingPoint;
    private SpringJoint joint;
    public float horizontalThrustForce;
    public float forwardThrustForce;
    public float extendCableSpeed;
    Vector3 currentGrapplePostiton;


    [Header("Prediction")]
    public RaycastHit predictionHit;
    public float predictionShereCastRedius;
    public Transform predictionPoint;
    private void Start()
    {
        playerMovement = GetComponent<PlayerMovement>();
        rb = GetComponent<Rigidbody>();
        playerMovement.OnSwingUp += StopSwing;
        playerMovement.OnSwingDown += StartSwing;
        playerMovement.OnJumpAction += OnJumpHandler;
    }

    private void OnJumpHandler(bool obj)
    {
        jumpRequest = obj;
    }
    private void Update()
    {
        if(joint != null)
        {
            OdmGearMovement();
        }
        CheckForSwingPoints();
    }
    private void OnApplicationQuit()
    {
        playerMovement.OnSwingUp -= StopSwing;
        playerMovement.OnSwingDown -= StartSwing;
    }
    private void LateUpdate()
    {
        DrawRope();
    }
    void StartSwing()
    {
        if (predictionHit.point == Vector3.zero) return;

        playerMovement.swinging = true;
        swingPoint = predictionHit.point;
        joint = this.gameObject.AddComponent<SpringJoint>();
        joint.autoConfigureConnectedAnchor = false;
        joint.connectedAnchor = swingPoint;

        float distanceFromPoint = Vector3.Distance(transform.position, swingPoint);
        joint.maxDistance = distanceFromPoint * 0.8f;
        joint.minDistance = distanceFromPoint * 0.2f;
        joint.spring = 4.5f;
        joint.damper = 7f;
        joint.massScale = 4.5f;
        lr.enabled = true;
        lr.positionCount = 2;
        currentGrapplePostiton = gunTip.position;
    }

    void OdmGearMovement()
    {
        inputDirection = playerMovement.inputDirection;
        if (inputDirection.x >0f)
        {
            rb.AddForce(orientation.right * horizontalThrustForce * Time.deltaTime);
        }
        if(inputDirection.x < 0f)
        {
            rb.AddForce(-orientation.right * horizontalThrustForce * Time.deltaTime);
        }
        if(inputDirection.y > 0f)
        {
            rb.AddForce(orientation.forward * forwardThrustForce * Time.deltaTime);
        }
        if (jumpRequest)
        {
            Vector3 directionToPoint = swingPoint - transform.position;
            rb.AddForce(directionToPoint.normalized * forwardThrustForce * Time.deltaTime);
            float distanceFromPoint = Vector3.Distance(transform.position, swingPoint);
            joint.maxDistance = distanceFromPoint * 0.8f;
            joint.minDistance = distanceFromPoint * 0.25f;
        }
        if(inputDirection.y < 0f)
        {
            float extendedDistanceFromPoint = Vector3.Distance(transform.position, swingPoint) + extendCableSpeed;
            joint.maxDistance = extendedDistanceFromPoint * 0.8f;
            joint.minDistance = extendedDistanceFromPoint * 0.25f;
        }
    }
    void StopSwing()
    {
        lr.positionCount = 0;
        Destroy(joint);
        lr.enabled = false;
        playerMovement.swinging = false;
    }
    void DrawRope()
    {
        if (!joint)
        {
            return;
        }
        if(lr && lr.positionCount >= 2)
        {
            currentGrapplePostiton = Vector3.Lerp(currentGrapplePostiton, swingPoint, Time.deltaTime * 8f);
            lr.SetPosition(0, gunTip.position);
            lr.SetPosition(1, currentGrapplePostiton);

        }
    }

    void CheckForSwingPoints()
    {
        if(joint != null)
        {
            return;
        }
        RaycastHit sphereHit;
        Physics.SphereCast(cam.position, predictionShereCastRedius, cam.forward, out sphereHit, maxSwingDistance, whatIsGarppleable);
        RaycastHit rayHit;
        Physics.Raycast(cam.position, cam.forward, out rayHit, maxSwingDistance, whatIsGarppleable);
        Vector3 realHitPoint;
        if(rayHit.point != Vector3.zero)
        {
            realHitPoint = rayHit.point;
        }else if(sphereHit.point != Vector3.zero)
        {
            realHitPoint = sphereHit.point;
        }
        else
        {
            realHitPoint = Vector3.zero;
        }

        if(realHitPoint != Vector3.zero)
        {
            predictionPoint.gameObject.SetActive(true);
            predictionPoint.position = realHitPoint;
        }
        else
        {   
            predictionPoint.gameObject.SetActive(false);
        }

        predictionHit = rayHit.point == Vector3.zero ? sphereHit:rayHit;
    }

}
